Skip to content

FEAT: TVP support for async queries - #814

Open
Subrata (subrata-ms) wants to merge 19 commits into
mainfrom
subrata-ms/AQEIntegration
Open

Subrata (subrata-ms) wants to merge 19 commits into
mainfrom
subrata-ms/AQEIntegration

Conversation

@subrata-ms

@subrata-ms Subrata (subrata-ms) commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Work Item / Issue Reference

AB#47998,48435

GitHub Issue: #<ISSUE_NUMBER>


Summary

This pull request introduces several improvements and enhancements to the mssql_python.async_query async API, focusing on better native type integration, more robust and consistent cursor behavior, and improved handling of parameter iterables in executemany. It also expands the test suite to cover these changes and edge cases.

Key highlights include:

  • Lazy exposure of native SQL type hints and the table-valued parameter constructor.
  • Improved type and error handling for parameter iterables in executemany.
  • New and clarified properties and behaviors for async cursors, including a closed property.
  • Expanded and refined test coverage for new and existing features.

Native type integration and exports

  • Added lazy exports for native SQL type hints (SQL_MONEY, SQL_SMALLMONEY, SQL_XML, SQL_JSON, SQL_VECTOR) and the internal _TableValuedParameter constructor, with dynamic loading and error reporting if the native dependency is missing. These are now included in __all__ and dir() as appropriate.
  • Added tests to verify that these native exports are correctly exposed, loaded only when accessed, and report missing features properly.

AsyncCursor API and behavior improvements

  • Added a closed property to async cursors to reflect both wrapper and parent connection state, with tests for idempotency and parent connection closure propagation.
  • Clarified and enhanced docstrings for nextset, close, description, and rowcount to document async-specific behaviors and differences from the synchronous API.
  • Added tests to ensure nextset correctly tracks rowcount and description per result, including edge cases.

Executemany and parameter iterable handling

  • Changed executemany signatures to accept any synchronous iterable (not just sequences), improved error handling for non-iterables, and ensured iterables are consumed only once. Streaming/asynchronous iterables are explicitly not supported.
  • Added tests for handling of empty generators, iterator failures, and correct consumption of parameter iterables.

Logging and test adjustments

  • Updated logging to remove reliance on batch_count for executemany and adjusted test expectations accordingly.
  • Minor imports and test harness updates to support new features.

These changes make the async query API more robust, Pythonic, and consistent with both native and DB-API expectations.

Expose native TVP and SQL type-hint helpers lazily. Support iterable executemany batches and preserve result state on iteration failure. Add cursor closed-state reporting, document async result contracts, and cover these behaviors with async integration tests.
Copilot AI lite review requested due to automatic review settings September 24, 2026 10:08
@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

PR Performance Report

✅ No regression detected

No consistent slowdowns detected across all 2 environments.

0 IMPROVEMENTS 0 SLOWDOWNS 2/2 ENVIRONMENTS

Coverage: 2 of 2 environments completed. Advisory result; does not block merging.

Performance diagnostics

Phase times are inclusive diagnostics and must not be added together. They identify where measured time changed, not why it changed.

No affected phases or call-count changes were recorded.

All database tasks and timings

Unix / SQL Server 2022

Database task Before After Paired change Result
Connection opening 10.666 ms 10.435 ms -2.6% no signal
SELECT queries 1.083 ms 1.108 ms +1.2% no signal
Row insertion 32.370 ms 32.564 ms +0.3% no signal
Executemany inserts 159.611 ms 161.220 ms +1.6% no signal
Fetch-all queries 121.276 ms 122.200 ms +0.9% no signal
Row-by-row fetching 14.387 ms 14.495 ms +1.8% no signal
Batched row fetching 115.964 ms 118.329 ms +1.9% no signal
Transaction commit and rollback 106.841 ms 106.849 ms -0.5% no signal
Arrow row fetching 89.081 ms 90.372 ms +1.8% no signal
100,000-row insertion 442.614 ms 446.621 ms -0.9% no signal
Row fetching in batches of 100 120.885 ms 123.485 ms +3.6% no signal
Row fetching in batches of 10,000 136.285 ms 141.262 ms +2.9% no signal
Repeated positional queries 31.956 ms 31.833 ms +0.2% no signal
Repeated named-parameter queries 34.673 ms 34.592 ms -0.2% no signal
Legacy 100,000-row insertion 350.685 ms 352.885 ms -0.3% no signal
Insertion with explicit input sizes 485.254 ms 484.818 ms -0.5% no signal
Joined aggregation queries 177.106 ms 176.703 ms +0.2% no signal
Large joined-result fetching 188.729 ms 191.989 ms +1.1% no signal
1.2-million-row fetching 3445.271 ms 3447.983 ms -0.5% no signal
Common table expression queries 5.388 ms 5.428 ms +0.6% no signal
256 KiB VARCHAR(MAX) / fetchall() 1.663 ms 1.623 ms +1.3% no signal
10,000 scalar values / fetchval() (debug disabled) 107.807 ms 106.825 ms -0.8% no signal

Unix / SQL Server 2025

Database task Before After Paired change Result
Connection opening 97.035 ms 97.411 ms +0.1% no signal
SELECT queries 1.092 ms 1.090 ms +2.4% no signal
Row insertion 32.620 ms 32.941 ms +0.2% no signal
Executemany inserts 153.337 ms 154.642 ms -1.1% no signal
Fetch-all queries 123.550 ms 123.551 ms +2.2% no signal
Row-by-row fetching 14.511 ms 14.424 ms -0.6% no signal
Batched row fetching 118.297 ms 118.562 ms +0.8% no signal
Transaction commit and rollback 109.324 ms 109.162 ms -0.2% no signal
Arrow row fetching 88.852 ms 92.003 ms +2.5% no signal
100,000-row insertion 433.743 ms 436.543 ms -0.7% no signal
Row fetching in batches of 100 123.952 ms 123.992 ms +0.0% no signal
Row fetching in batches of 10,000 133.413 ms 145.389 ms +9.0% no signal
Repeated positional queries 31.838 ms 32.164 ms +0.8% no signal
Repeated named-parameter queries 34.333 ms 34.300 ms +0.3% no signal
Legacy 100,000-row insertion 351.383 ms 359.405 ms +0.9% no signal
Insertion with explicit input sizes 490.991 ms 482.536 ms -0.9% no signal
Joined aggregation queries 160.473 ms 160.359 ms -0.0% no signal
Large joined-result fetching 197.605 ms 195.034 ms +1.3% no signal
1.2-million-row fetching 3471.413 ms 3463.882 ms +0.1% no signal
Common table expression queries 5.220 ms 5.251 ms -1.1% no signal
256 KiB VARCHAR(MAX) / fetchall() 2.166 ms 2.120 ms +2.8% no signal
10,000 scalar values / fetchval() (debug disabled) 107.503 ms 107.446 ms -0.1% no signal
Build and measurement details

ADO build 188224

PR head: ac02c7055f1fd7e159d61009337a2cc1eb8bd6f7
Base: 9db65aeb49f8cd7c49c60f5d1ac56d0113f8e77b
Measured merge: f61c242057cd4d24baec679cd75176f50464399f

  • Unix / SQL Server 2022: Python 3.12.3, x86_64, SQL 16.0.4305.5; 5 paired comparisons and 1 warmup.
  • Unix / SQL Server 2025: Python 3.12.3, x86_64, SQL 17.0.5035.5; 5 paired comparisons and 1 warmup.

A consistent change requires more than 20% median paired movement, at least 1 ms between the median runtimes, and at least 80% of pairs exceeding the relative threshold in the same direction. A slowdown without enough pair agreement is reported as inconsistent.

The displayed change is the median of paired before-and-after ratios. It is not recalculated from the two displayed median runtimes.

Both revisions use profiling-enabled builds on the same agent and database, with alternating order and discarded warmups. Results are diagnostic and do not represent production-wheel latency.

Raw samples and logs are attached to the ADO run as profiler-* artifacts.

@subrata-ms Subrata (subrata-ms) changed the title FEAT: Add preview TVP support for async queries FEAT: Add TVP support for async queries Sep 24, 2026
@github-actions github-actions Bot added the pr-size: medium Moderate update size label Sep 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Guard the optional native dependency test and complete the required PR metadata.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
What changed in this PR

Adds preview async TVP/type-hint support, iterable executemany, cursor state reporting, and expanded integration tests.

Changes:

  • Adds lazy native TVP and SQL type-hint exports.
  • Supports synchronous iterable batches and failure-result preservation.
  • Documents and tests async cursor/result behavior.
File Summary
tests/​AsyncTest/​test_006_async_execute.py Tests iterable batches, TVPs, and type hints.
tests/​AsyncTest/​test_005_async_cursor.py Tests cursor state and result behavior.
tests/​AsyncTest/​test_004_async_logging.py Updates execution logging expectations.
tests/​AsyncTest/​test_001_async_query_native.py Tests native exports. Finding: moderate, 2 votes—add an importorskip guard for the optional dependency at lines 55 and 65.
mssql_python/​async_query/​async_execute.py Adds iterable batch execution and failure handling.
mssql_python/​async_query/​async_cursor.py Adds cursor state and result-contract documentation.
mssql_python/​async_query/​__init__.py Adds lazy native exports. Finding: nit, 1 vote—replace PR placeholders and empty summary with valid metadata.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 24, 2026 10:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Fix the indentation error that prevents the native async test module from being collected.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
Resolved since last review (1)

Copilot AI review requested due to automatic review settings September 24, 2026 11:05
@github-actions github-actions Bot added pr-size: large Substantial code update and removed pr-size: medium Moderate update size labels Sep 24, 2026
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Fix the indentation error that prevents the native async test module from being collected.

Review effort: Lite
Findings: None

Resolved since last review (1)

Copilot AI review requested due to automatic review settings September 24, 2026 11:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Cancellation handling and version-specific collation test behavior need correction.

Review effort: Lite
Findings: None

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

📊 Code Coverage Report

🔥 Diff Coverage

83%


🎯 Overall Coverage

85%


📈 Total Lines Covered: 9533 out of 11195
📁 Project: mssql-python


Diff Coverage

Diff: main...HEAD, staged and unstaged changes

  • mssql_python/async_query/init.py (100%)
  • mssql_python/async_query/async_cursor.py (100%)
  • mssql_python/async_query/async_execute.py (50.0%): Missing lines 102-106

Summary

  • Total: 31 lines
  • Missing: 5 lines
  • Coverage: 83%

mssql_python/async_query/async_execute.py

Lines 98-110

   98     iteration_failed = False
   99 
  100     def parameter_rows():
  101         nonlocal iteration_failed
! 102         try:
! 103             yield from seq_of_parameters
! 104         except BaseException:
! 105             iteration_failed = True
! 106             raise
  107 
  108     logger.debug(
  109         "AsyncCursor.executemany: starting; use_prepare=%s",
  110         use_prepare,


📋 Files Needing Attention

📉 Files with overall lowest coverage (click to expand)
mssql_python.pybind.performance_counter.hpp: 0.7%
mssql_python.pybind.logger_bridge.cpp: 57.9%
mssql_python.pybind.ddbc_bindings.h: 62.6%
mssql_python.pybind.logger_bridge.hpp: 70.8%
mssql_python.pybind.connection.connection_pool.cpp: 81.3%
mssql_python.pybind.connection.connection.cpp: 84.1%
mssql_python.logging.py: 86.2%
mssql_python.pooling.py: 90.1%
mssql_python.async_query.async_execute.py: 91.5%
mssql_python.pybind.fetch_temporal.hpp: 92.1%

🔗 Quick Links

⚙️ Build Summary 📋 Coverage Details

View Azure DevOps Build

Browse Full Coverage Report

Copilot AI review requested due to automatic review settings September 24, 2026 11:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The pinned native core dependency must be updated or the new exports gated before approval.

Review effort: Lite
Findings: None

Copilot AI balanced review requested due to automatic review settings October 8, 2026 10:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Native-backed async behavior and live SQL integration require final human validation despite no confirmed defect.

0 open findings

🧠 Review effort: Balanced


Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.

Copilot AI balanced review requested due to automatic review settings October 12, 2026 11:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The pinned native dependency still rejects valid numeric and scaled temporal TVP metadata, leaving advertised TVP support incomplete.

0 open findings

🧠 Review effort: Balanced

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For context, I have narrowed #789 to the shared TableValuedParameter definition, synchronous ODBC streaming, and sync documentation/tests. I removed the async adapter and async TVP tests from that PR to avoid duplicating the integration here.

The design direction remains one public TVP value with separate ODBC and TDS implementations. Could we build the async support here around that shared class, while retaining the native work already in this PR?

Shared interface

  • Use mssql_python.TableValuedParameter(type_name, columns, rows, *, schema=None) as the application-facing value. Keep _TableValuedParameter internal.
  • Preserve the shared column format (name, sql_type[, size_or_precision[, scale]]), positional binding, and explicit metadata. Translate this into native column triples inside async_execute.py, including the shared 0 = MAX convention.
  • Preserve the existing identity and empty-input rules: schema.Type or a separate schema argument; [] for an empty table; None for nullable cells, not for the table itself. Reuse the shared validation rather than adding a parallel contract.

Initial async scope

  • Start with materialized list/tuple rows. Reject synchronous iterators before consumption rather than silently materializing them. Native streaming remains a separate follow-up.
  • Support the shared value through execute(), including named parameters, surrounding scalars and multiple TVPs. Keep TVP executemany() and TVP setinputsizes() overrides outside this initial slice; this does not affect ordinary non-TVP batch support.
  • Document the current Rust 0.3.0 metadata restrictions: DECIMAL/NUMERIC(18,10) and temporal scale 7. Broader support needs a native fix and dependency update, not silent changes to the caller's declarations.

Landing and coverage

  • Land the shared definition through #789, then integrate the async adapter here without copying the class. The independent cursor and type-hint improvements do not need to be rewritten.
  • Use the shared wrapper in customer examples and async acceptance tests. Mirror the applicable cases in tests/test_028_table_valued_parameters.py: empty/NULL input, supported types, Unicode/binary/MAX values, mixed parameters, reuse and error recovery.
  • Include rejection-before-consumption and caller-input preservation. Keep native-constructor tests as internal coverage and large memory workloads in stress coverage.

This keeps the implementation ownership separate while giving callers the same TVP interface.

Copilot AI balanced review requested due to automatic review settings October 26, 2026 06:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Iterable exceptions can be misclassified as driver failures, and the new native-only constructor test lacks a missing-dependency skip guard.

2 open issues

Review effort: Balanced

Comment on lines +104 to +106
except BaseException:
iteration_failed = True
raise
Copilot AI balanced review requested due to automatic review settings October 26, 2026 08:16
Subrata (subrata-ms) and others added 2 commits October 26, 2026 13:47
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A new native-dependent metadata test lacks a local dependency guard and fails instead of skipping when the Rust runtime is unavailable.

2 open issues
1 resolved since last review

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 26, 2026 08:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Native-backed TVP and batch behavior need confirmed live SQL Server validation and human review before approval.

1 open issue
1 resolved since last review

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings October 29, 2026 08:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Iterator-origin exceptions are still translated rather than propagated unchanged, and an unguarded native-only test fails instead of skipping when the native dependency is unavailable.

2 open issues

Review effort: Balanced



def test_async_tvp_accepts_numeric_and_temporal_column_metadata():
from mssql_python.async_query import _TableValuedParameter
Copilot AI balanced review requested due to automatic review settings November 2, 2026 09:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Parameter iterator failures can still be translated from RuntimeError to InterfaceError during call-time or await-time consumption, losing the original exception’s type and identity.

3 open issues

Review effort: Balanced

Comment on lines +122 to 124
if not iteration_failed:
_reconcile_failed_execution(cursor, error)
raise
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-size: large Substantial code update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants